package org.texteasy;

import java.util.ArrayList;
import java.util.HashSet;

import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.Resources;
import android.database.CharArrayBuffer;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.Contacts.Groups;
import android.provider.Contacts.Phones;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.CheckBox;
import android.widget.ListView;
import android.widget.ResourceCursorAdapter;
import android.widget.TextView;

public class BulkMessenger extends ListActivity implements
		DialogInterface.OnClickListener, View.OnClickListener {

	private static final String TAG = "BulkMessenger";

	private final static int NEW_MESSAGE_ID = Menu.FIRST;
	private final static int DISPLAY_GROUP_ID = Menu.FIRST + 1;
	private final static int SELECT_ALL_ID = Menu.FIRST + 2;
	private final static int UNSELECT_ALL_ID = Menu.FIRST + 3;
	private final static int EDIT_SETTINGS = Menu.FIRST + 4;

	// private String[] projection = new String[] {People._ID,People.NAME,
	// People.TYPE, People.NUMBER};
	private String[] groupsProjection = new String[] { Groups._ID,
			Groups.SYSTEM_ID, Groups.NAME };
	// private Uri contacts = People.CONTENT_URI;
	private HashSet<String> selectedIds = new HashSet<String>();
	private long selectedGroupId = -1;
	private static final String SELECTED_GROUP_ID_PREFERENCE = "SelectedGroupId";
	private int selectedGroupIndex;
	private ArrayList<Long> displayGroupIds;
	private TextEasyDBAdapter dbAdaptor;
	private AlertDialog groupDialog;
	private boolean refreshQueryOnResume = false;

	private static final String PHONE_TYPE_HOME_PREFERENCE = "PHONE_TYPE_HOME";
	private static final String PHONE_TYPE_MOBILE_PREFERENCE = "PHONE_TYPE_MOBILE";
	private static final String PHONE_TYPE_WORK_PREFERENCE = "PHONE_TYPE_WORK";
	private static final String PHONE_TYPE_OTHER_PREFERENCE = "PHONE_TYPE_OTHER";
	private static final String PHONE_TYPE_CUSTOM_PREFERENCE = "PHONE_TYPE_CUSTOM";

	private static final String PREV_PHONE_TYPE_HOME_PREFERENCE = "PREV_PHONE_TYPE_HOME";
	private static final String PREV_PHONE_TYPE_MOBILE_PREFERENCE = "PREV_PHONE_TYPE_MOBILE";
	private static final String PREV_PHONE_TYPE_WORK_PREFERENCE = "PREV_PHONE_TYPE_WORK";
	private static final String PREV_PHONE_TYPE_OTHER_PREFERENCE = "PREV_PHONE_TYPE_OTHER";
	private static final String PREV_PHONE_TYPE_CUSTOM_PREFERENCE = "PREV_PHONE_TYPE_CUSTOM";

	static final int ID_COLUMN_INDEX = 0;
	static final int NAME_COLUMN_INDEX = 1;
	static final int TYPE_COLUMN_INDEX = 2;
	static final int NUMBER_COLUMN_INDEX = 3;
	static final int UNIQUE_NUMBER_COLUMN_INDEX = 4;

	private static final int GROUPS_COLUMN_INDEX_ID = 0;
	private static final int GROUPS_COLUMN_INDEX_SYSTEM_ID = 1;
	private static final int GROUPS_COLUMN_INDEX_NAME = 2;

	private static final int DISPLAY_GROUPS_ALL_CONTACTS = 0;

	private ArrayList<Integer> selectedPhoneTypes = null;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
		setContentView(R.layout.contacts_list);

		findViewById(R.id.btn_new_message).setOnClickListener(this);
		findViewById(R.id.btn_options).setOnClickListener(this);

		this.dbAdaptor = new TextEasyDBAdapter(this);
		this.dbAdaptor.open();

		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(this);
		selectedGroupId = prefs.getLong(SELECTED_GROUP_ID_PREFERENCE, -1);

		checkPhoneTypePreferences(prefs);
		updateSelectedPhoneTypes(prefs);
		boolean storeSelections = prefs.getBoolean(
				getString(R.string.REMEMBER_SELECTIONS), false);
		if (!storeSelections) {

			if (getLastNonConfigurationInstance() != null) {
				this.selectedIds = (HashSet<String>) getLastNonConfigurationInstance();
			}
		}

		ListView listView = getListView();
		listView.setItemsCanFocus(false);
		listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
		listView.setTextFilterEnabled(true);
		queryContacts();

	}

	private void checkPhoneTypePreferences(SharedPreferences prefs) {

		boolean needsUpdate = false;
		Editor editor = prefs.edit();
		if (!prefs.contains(PHONE_TYPE_CUSTOM_PREFERENCE)) {
			editor.putBoolean(PHONE_TYPE_CUSTOM_PREFERENCE, true);
			needsUpdate = true;
		}

		if (!prefs.contains(PHONE_TYPE_HOME_PREFERENCE)) {
			editor.putBoolean(PHONE_TYPE_HOME_PREFERENCE, true);
			needsUpdate = true;
		}

		if (!prefs.contains(PHONE_TYPE_MOBILE_PREFERENCE)) {
			editor.putBoolean(PHONE_TYPE_MOBILE_PREFERENCE, true);
			needsUpdate = true;
		}

		if (!prefs.contains(PHONE_TYPE_OTHER_PREFERENCE)) {
			editor.putBoolean(PHONE_TYPE_OTHER_PREFERENCE, true);
			needsUpdate = true;
		}

		if (!prefs.contains(PHONE_TYPE_WORK_PREFERENCE)) {
			editor.putBoolean(PHONE_TYPE_WORK_PREFERENCE, true);
			needsUpdate = true;
		}

		if (needsUpdate) {
			editor.commit();
		}
	}

	/**
	 * Utility method for displaying querying contacts and displaying them
	 * 
	 * @param nameClause
	 */
	private void queryContacts() {

		Cursor managedCursor = ContactsAccessor.getInstance()
				.queryContacts(getContentResolver(), null, selectedGroupId,
						selectedPhoneTypes);

		ResourceCursorAdapter contactsAdaptor = new ContactItemListAdapter(
				this, R.layout.contacts_row, managedCursor);
		setListAdapter(contactsAdaptor);
	}

	@Override
	protected void onPause() {
		super.onPause();

		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(this);
		boolean storeSelections = prefs.getBoolean(
				getString(R.string.REMEMBER_SELECTIONS), false);

		if (storeSelections) {
			HashSet<String> selectedIds = this.selectedIds;
			if (selectedIds != null && !selectedIds.isEmpty()) {
				this.dbAdaptor.persistSelectedIds(selectedIds);
			}
		}

		this.dbAdaptor.close();

		if (groupDialog != null && groupDialog.isShowing()) {
			groupDialog.dismiss();
		}

	}

	@Override
	protected void onDestroy() {
		super.onDestroy();

		if (this.dbAdaptor.isOpen()) {
			this.dbAdaptor.close();
		}
	}

	@Override
    protected void onResume() {
    	super.onResume();
    	this.dbAdaptor.open();
    	
    	SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        boolean storeSelections = prefs.getBoolean(getString(R.string.REMEMBER_SELECTIONS), false);
        if(storeSelections){
        	this.selectedIds = this.dbAdaptor.getSelectedIdsAndClear();
        }
        
        boolean clearSelectedIds = false;
        //if the prev preference is true and the current is false then the
        //user has filtered phones and any selections are invalid
        if(prefs.getBoolean(PREV_PHONE_TYPE_CUSTOM_PREFERENCE, false) && 
    		!prefs.getBoolean(PHONE_TYPE_CUSTOM_PREFERENCE, false)){
        	
        	clearSelectedIds =true;
        }else if(prefs.getBoolean(PREV_PHONE_TYPE_HOME_PREFERENCE, false) && 
    		!prefs.getBoolean(PHONE_TYPE_HOME_PREFERENCE, false)){
        	
        	clearSelectedIds =true;
        }else if(prefs.getBoolean(PREV_PHONE_TYPE_MOBILE_PREFERENCE, false) && 
        		!prefs.getBoolean(PHONE_TYPE_MOBILE_PREFERENCE, false)){
            	
        	clearSelectedIds =true;
        }else if(prefs.getBoolean(PREV_PHONE_TYPE_OTHER_PREFERENCE, false) && 
        		!prefs.getBoolean(PHONE_TYPE_OTHER_PREFERENCE, false)){
            	
        	clearSelectedIds =true;
        }else if(prefs.getBoolean(PREV_PHONE_TYPE_WORK_PREFERENCE, false) && 
        		!prefs.getBoolean(PHONE_TYPE_WORK_PREFERENCE, false)){
            	
        	clearSelectedIds =true;
        }
        
        if(clearSelectedIds){
        	this.selectedIds = new HashSet<String>();
        	backupPhoneTypePreferences();
        }
        
        //hide the menu bar if HIDE_MENU_BAR is checked
        if(prefs.getBoolean(getString(R.string.HIDE_MENU_BAR), false)){
        	findViewById(R.id.btn_bar).setVisibility(View.GONE);
        }else{
        	findViewById(R.id.btn_bar).setVisibility(View.VISIBLE);
        }
        
        updateSelectedPhoneTypes(prefs);
        
        if(refreshQueryOnResume){
        	refreshQueryOnResume = false;
        	queryContacts();
        }
    }

	private void updateSelectedPhoneTypes(SharedPreferences prefs) {
		// update the selectedPhoneTypes
		ArrayList<Integer> phoneTypesToFilter = new ArrayList<Integer>();
		if (prefs.getBoolean(PHONE_TYPE_CUSTOM_PREFERENCE, false)) {
			phoneTypesToFilter.add(Phones.TYPE_CUSTOM);
		}

		if (prefs.getBoolean(PHONE_TYPE_HOME_PREFERENCE, false)) {
			phoneTypesToFilter.add(Phones.TYPE_HOME);
		}

		if (prefs.getBoolean(PHONE_TYPE_MOBILE_PREFERENCE, false)) {
			phoneTypesToFilter.add(Phones.TYPE_MOBILE);
		}

		if (prefs.getBoolean(PHONE_TYPE_OTHER_PREFERENCE, false)) {
			phoneTypesToFilter.add(Phones.TYPE_OTHER);
		}

		if (prefs.getBoolean(PHONE_TYPE_WORK_PREFERENCE, false)) {
			phoneTypesToFilter.add(Phones.TYPE_WORK);
		}

		this.selectedPhoneTypes = phoneTypesToFilter;
	}

	@Override
	public Object onRetainNonConfigurationInstance() {

		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(this);
		boolean storeSelections = prefs.getBoolean(
				getString(R.string.REMEMBER_SELECTIONS), false);

		if (!storeSelections) {
			return this.selectedIds;
		} else {
			return null;
		}
	}

	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		super.onPrepareOptionsMenu(menu);

		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(this);
		// Only show new Message in options if hide menu bar is checked
		boolean hidMenuBar = prefs.getBoolean(
				getString(R.string.HIDE_MENU_BAR), false);
		if (hidMenuBar && menu.findItem(NEW_MESSAGE_ID) == null) {
			menu.add(Menu.NONE, NEW_MESSAGE_ID, 0, R.string.NEW_MESSAGE);
		} else if (!hidMenuBar) {
			menu.removeItem(NEW_MESSAGE_ID);
		}

		return true;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		menu.add(Menu.NONE, DISPLAY_GROUP_ID, 1, R.string.DISPLAY_GROUPS);
		menu.add(Menu.NONE, SELECT_ALL_ID, 2, R.string.SELECT_ALL);
		menu.add(Menu.NONE, UNSELECT_ALL_ID, 3, R.string.UNSELECT_ALL);
		menu.add(Menu.NONE, EDIT_SETTINGS, 3, R.string.SETTINGS);
		return true;
	}

	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {
		super.onListItemClick(l, v, position, id);

		Cursor cursor = (Cursor) getListView().getItemAtPosition(position);
		long contactId = cursor.getLong(ID_COLUMN_INDEX);
		long uniqueNumberId = -1;

		if (!cursor.isNull(TYPE_COLUMN_INDEX)) {
			uniqueNumberId = cursor.getInt(UNIQUE_NUMBER_COLUMN_INDEX);
		}

		String clickedContact = contactId + "|" + uniqueNumberId;

		HashSet<String> selectedIds = this.selectedIds;
		if (selectedIds.contains(clickedContact)) {
			selectedIds.remove(clickedContact);
		} else {
			selectedIds.add(clickedContact);
		}
	}

	Cursor doFilter(String filter) {
		final ContentResolver resolver = getContentResolver();

		return ContactsAccessor.getInstance().queryContacts(resolver, filter,
				selectedGroupId, selectedPhoneTypes);
	}

	public void onClick(View v) {

		switch (v.getId()) {
		case R.id.btn_new_message: {
			createNewMessage();
			break;
		}
		case R.id.btn_options: {
			getWindow().openPanel(Window.FEATURE_OPTIONS_PANEL,
					new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
			getWindow().openPanel(Window.FEATURE_OPTIONS_PANEL,
					new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU));
			break;
		}
		}

	}

	public void onClick(DialogInterface dialogInterface, int which) {

		if (which == DialogInterface.BUTTON1) {

			if (selectedGroupIndex == DISPLAY_GROUPS_ALL_CONTACTS) {
				selectedGroupId = -1l;
			} else {
				selectedGroupId = this.displayGroupIds.get(selectedGroupIndex);
			}

			SharedPreferences prefs = PreferenceManager
					.getDefaultSharedPreferences(this);
			prefs.edit().putLong(SELECTED_GROUP_ID_PREFERENCE, selectedGroupId)
					.commit();

			this.selectedIds = new HashSet<String>();
			getListView().setFilterText("A");
			getListView().clearTextFilter();
		} else {
			// user just selected an option
			selectedGroupIndex = which;
		}

	}

	@Override
	public boolean onMenuItemSelected(int featureId, MenuItem item) {

		switch (item.getItemId()) {
		case NEW_MESSAGE_ID:
			createNewMessage();
			break;
		case DISPLAY_GROUP_ID:
			createGroupAlertDialog();
			break;
		case SELECT_ALL_ID:
			selectAll();
			break;
		case EDIT_SETTINGS:
			refreshQueryOnResume = true;

			backupPhoneTypePreferences();
			startActivity(new Intent(this, TextEasyPreferences.class));
			return (true);
		case UNSELECT_ALL_ID:
			this.selectedIds = new HashSet<String>();
			getListView().setFilterText("A");
			getListView().clearTextFilter();
			break;
		}

		return super.onMenuItemSelected(featureId, item);
	}

	private void backupPhoneTypePreferences() {
		// Before going to settings, save the phoneTypeFilters so that changes
		// can be detected. If there are changes(only true->false) then remove
		// any selected contacts. If we don't clear selected contacts on filter
		// changes then can send text to selected contact you can't see
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
		Editor editor = prefs.edit();

		editor.putBoolean(PREV_PHONE_TYPE_CUSTOM_PREFERENCE, prefs
				.getBoolean(PHONE_TYPE_CUSTOM_PREFERENCE, false));
		editor.putBoolean(PREV_PHONE_TYPE_HOME_PREFERENCE, prefs
				.getBoolean(PHONE_TYPE_HOME_PREFERENCE, false));
		editor.putBoolean(PREV_PHONE_TYPE_MOBILE_PREFERENCE, prefs
				.getBoolean(PHONE_TYPE_MOBILE_PREFERENCE, false));
		editor.putBoolean(PREV_PHONE_TYPE_OTHER_PREFERENCE, prefs
				.getBoolean(PHONE_TYPE_OTHER_PREFERENCE, false));
		editor.putBoolean(PREV_PHONE_TYPE_WORK_PREFERENCE, prefs
				.getBoolean(PHONE_TYPE_WORK_PREFERENCE, false));

		editor.commit();
	}

	private void selectAll() {

		// Log.e(TAG, "selectAll start");

		Cursor managedCursor = ContactsAccessor.getInstance()
				.queryContacts(getContentResolver(), null, selectedGroupId,
						selectedPhoneTypes);

		// Log.e(TAG, "selectAll after query");

		HashSet<String> selectedIds = this.selectedIds;

		while (managedCursor.moveToNext()) {

			Long id = managedCursor.getLong(ID_COLUMN_INDEX);
			Long uniquePhoneId = managedCursor
					.getLong(UNIQUE_NUMBER_COLUMN_INDEX);

			// if(!selectedIds.contains(id)){
			selectedIds.add(id + "|" + uniquePhoneId);
			// }
		}

		// Log.e(TAG, "selectAll after selection");

		getListView().setFilterText("A");
		getListView().clearTextFilter();

		// Log.e(TAG, "after textFilter update");

	}

	/*
	 * private void createSignatureDialog(){
	 * 
	 * LayoutInflater factory = LayoutInflater.from(this); final View
	 * textEntryView = factory.inflate(R.layout.signature_dialog_entry, null);
	 * 
	 * signatureDialog = new AlertDialog.Builder(this)
	 * .setTitle(R.string.ADD_SIGNATURE_TITLE)
	 * .setPositiveButton(android.R.string.ok, this)
	 * .setNegativeButton(android.R.string.cancel, null).
	 * setView(textEntryView).create();
	 * 
	 * signatureDialog.show(); }
	 */

	private void createGroupAlertDialog() {

		Cursor cursor = ContactsAccessor.getInstance().queryGroups(
				getContentResolver());

		ArrayList<CharSequence> groups = new ArrayList<CharSequence>();
		ArrayList<Long> groupIds = new ArrayList<Long>();

		groups.add(DISPLAY_GROUPS_ALL_CONTACTS,
				getString(R.string.ALL_CONTACTS_WITH_PHONES));
		groupIds.add(DISPLAY_GROUPS_ALL_CONTACTS, -1l);

		int selectedIndex = 0;
		int i = 1;
		while (cursor.moveToNext()) {

			String systemId = cursor.getString(GROUPS_COLUMN_INDEX_SYSTEM_ID);
			String name = cursor.getString(GROUPS_COLUMN_INDEX_NAME);
			Long groupId = cursor.getLong(GROUPS_COLUMN_INDEX_ID);

			Log.e("BulkMessenger", "groupId: " + groupId + " selectedGroupId: "
					+ selectedGroupId);

			if (cursor.isNull(GROUPS_COLUMN_INDEX_SYSTEM_ID)
					&& !Groups.GROUP_MY_CONTACTS.equals(systemId)) {
				groups.add(name);
			} else {
				// found the "My Contacts" this displays weird (System Group: My
				// Contacts)
				// so gave it a different name
				groups.add(getString(R.string.GROUP_NAME_MY_CONTACTS));
			}
			groupIds.add(groupId);
			if (selectedGroupId == groupId) {
				selectedIndex = i;
			}
			i++;
		}

		this.displayGroupIds = groupIds;
		CharSequence[] displayGroups = groups.toArray(new CharSequence[groups
				.size()]);

		Log.e("blah", selectedIndex + "");

		groupDialog = new AlertDialog.Builder(this).setTitle(
				R.string.SELECT_GROUP_TITLE).setPositiveButton(
				android.R.string.ok, this).setNegativeButton(
				android.R.string.cancel, null).setSingleChoiceItems(
				displayGroups, selectedIndex, this).create();
		groupDialog.show();
	}

	private void createNewMessage() {

		final HashSet<String> selectedIds = this.selectedIds;
		boolean contactSelected = false;
		if (!selectedIds.isEmpty()) {
			contactSelected = true;
		}

		final BulkMessenger that = this;

		if (contactSelected) {
			setProgressBarIndeterminateVisibility(true);
			new AsyncTask<Void, Void, String>() {
				@Override
				protected String doInBackground(Void... unused) {

					HashSet<Long> onlyIds = new HashSet<Long>();
					for (String selectedId : selectedIds) {
						String[] tokens = selectedId.split("\\|");
						onlyIds.add(new Long(tokens[0]));
					}

					Cursor managedCursor = ContactsAccessor.getInstance()
							.queryContactsForPhoneNumbers(getContentResolver(),
									onlyIds);

					// Log.e(TAG, "before iterating createNewMessage");

					StringBuffer buffer = new StringBuffer();
					if (managedCursor != null) {
						while (managedCursor.moveToNext()) {

							Long contactId = managedCursor.getLong(0);
							Long uniquePhoneId = managedCursor.getLong(2);

							if (selectedIds.contains(contactId + "|"
									+ uniquePhoneId)) {

								// skip null or blank values
								String phoneNumber = managedCursor
										.getString(managedCursor
												.getColumnIndexOrThrow(ContactsAccessor
														.getInstance()
														.getPhoneColumnIndex()));
								if (phoneNumber != null
										&& !"".equals(phoneNumber.trim()))
									buffer.append(phoneNumber).append(";");
							}
						}
					}

					// Log.e(TAG, "after query createNewMessage");
					if (buffer.length() != 0) {
						String numbers = buffer.substring(0,
								buffer.length() - 1);

						return numbers;
					} else {
						return null;
					}
				}

				@Override
				protected void onPostExecute(String result) {
					if (result != null) {
						Intent i = new Intent();
						i.setAction(Intent.ACTION_VIEW);
						i.setType("vnd.android-dir/mms-sms");
						i.putExtra("address", result);

						SharedPreferences prefs = PreferenceManager
								.getDefaultSharedPreferences(that);
						String signature = prefs.getString(
								getString(R.string.ADD_SIGNATURE_TITLE), "");
						if (signature != null && !signature.equals("")) {
							i.putExtra("sms_body", "\n" + signature);
						}

						// Log.e(TAG, "starting mms activity");
						setProgressBarIndeterminateVisibility(false);
						startActivity(i);
					} else {
						setProgressBarIndeterminateVisibility(false);
					}

				}
			}.execute();
		}

		// Log.e(TAG, "start createNewMessage");

		if (contactSelected) {

		}

	}

	final static class ContactListItemCache {
		public TextView nameView;
		public CharArrayBuffer nameBuffer = new CharArrayBuffer(128);
		public TextView labelView;
		public CharArrayBuffer labelBuffer = new CharArrayBuffer(128);
		public TextView numberView;
		public CharArrayBuffer numberBuffer = new CharArrayBuffer(128);
		public CheckBox checkBox;
		public boolean checked;

	}

	private final class ContactItemListAdapter extends ResourceCursorAdapter {

		private CharSequence[] mLocalizedLabels;
		private CharSequence mUnknownNameText;

		public ContactItemListAdapter(Context context, int resource,
				Cursor cursor) {
			super(context, resource, cursor);

			final Resources resources = context.getResources();
			mLocalizedLabels = resources
					.getStringArray(android.R.array.phoneTypes);

			mUnknownNameText = context.getText(android.R.string.unknownName);
		}

		@Override
		public View newView(Context context, Cursor cursor, ViewGroup parent) {
			final View view = super.newView(context, cursor, parent);

			final ContactListItemCache cache = new ContactListItemCache();
			cache.nameView = (TextView) view.findViewById(R.id.name);
			cache.labelView = (TextView) view.findViewById(R.id.label);
			cache.labelView.setCompoundDrawablePadding(3);
			cache.numberView = (TextView) view.findViewById(R.id.number);
			cache.checkBox = (CheckBox) view.findViewById(R.id.selected);
			view.setTag(cache);

			return view;

		}

		@Override
		public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
			return doFilter(constraint.toString());
		}

		@Override
		public void bindView(View view, Context context, Cursor cursor) {

			final ContactListItemCache cache = (ContactListItemCache) view
					.getTag();

			// Set the name
			cursor.copyStringToBuffer(NAME_COLUMN_INDEX, cache.nameBuffer);
			int size = cache.nameBuffer.sizeCopied;
			if (size != 0) {
				cache.nameView.setText(cache.nameBuffer.data, 0, size);
			} else {
				cache.nameView.setText(mUnknownNameText);
			}

			// Set the phone number
			TextView numberView = cache.numberView;
			cursor.copyStringToBuffer(NUMBER_COLUMN_INDEX, cache.numberBuffer);
			size = cache.numberBuffer.sizeCopied;
			if (size != 0) {
				numberView.setText(cache.numberBuffer.data, 0, size);
				numberView.setVisibility(View.VISIBLE);
			} else {
				numberView.setVisibility(View.GONE);
			}

			// Set the label
			int type = -1;
			TextView labelView = cache.labelView;
			if (!cursor.isNull(TYPE_COLUMN_INDEX)) {
				type = cursor.getInt(TYPE_COLUMN_INDEX);

				if (type != android.provider.ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM) {
					try {
						labelView.setText(mLocalizedLabels[type - 1]);
					} catch (ArrayIndexOutOfBoundsException e) {
						labelView
								.setText(mLocalizedLabels[android.provider.ContactsContract.CommonDataKinds.Phone.TYPE_HOME - 1]);
					}
				} else {
					// Set the text to a length of 0
					labelView.setText(cache.labelBuffer.data, 0, 0);
				}
			} else {
				// Set the text to a length of 0
				labelView.setText(cache.labelBuffer.data, 0, 0);
			}

			long uniqueNumberId = cursor.getLong(UNIQUE_NUMBER_COLUMN_INDEX);
			long contactId = cursor.getLong(ID_COLUMN_INDEX);
			if (selectedIds.contains(contactId + "|" + uniqueNumberId)) {
				cache.checkBox.setChecked(true);
			} else {
				cache.checkBox.setChecked(false);
			}

		}

	}

}